spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { Metadata } from "next";2import Link from "next/link";3import { notFound } from "next/navigation";4import { Breadcrumbs } from "@/components/entity/breadcrumbs";5import { EntityMap } from "@/components/entity/entity-map";6import { JsonLd } from "@/components/entity/json-ld";7import { ExtLink, KeyValueList } from "@/components/entity/kv";8import { SectionNav } from "@/components/entity/section-nav";9import { SourcesFooter } from "@/components/entity/sources-footer";10import { EventList } from "@/components/event-list";11import { ProvenanceTable } from "@/components/entity/provenance";12import { FacilityTable } from "@/components/facility-table";13import { ApiNote, HonestEmpty } from "@/components/intelligence/shared";14import { Badge, PrecisionBadge } from "@/components/ui/badge";15import { Page, Section } from "@/components/ui/card";16import { CountryFlag } from "@/components/ui/country-flag";17import { Metric, MetricCell, MetricRow } from "@/components/ui/metric";18import { EmptyState, OfflineNotice } from "@/components/ui/states";19import { RowLink, TBody, THead, Table, Td, Th, Tr } from "@/components/ui/table";20import { api } from "@/lib/api";21import { countryName, fmtDate, fmtInt } from "@/lib/format";22import { PRECISION_LABEL } from "@/lib/labels";23import { routes } from "@/lib/routes";24import { NOT_FOUND_METADATA, absoluteUrl, compactJsonLd, pageMetadata } from "@/lib/seo";25import { CLOUD_STATUS_LABEL, CloudStatusBadge, countryHrefMap } from "../cloud-format";2627export const revalidate = 300;2829type Params = Promise<{ slug: string }>;3031function describe(d: { provider: { name: string }; code: string; name: string; city: string | null; countryIso2: string | null; availabilityZones: number | null; launchedOn: string | null; status: string; isSovereign: boolean }): string {32 const parts = [`${d.provider.name} cloud region ${d.code} (${d.name})`];33 const place = [d.city, d.countryIso2 ? countryName(d.countryIso2) : null].filter(Boolean).join(", ");34 if (place) parts.push(`located in ${place}`);35 if (d.availabilityZones) parts.push(`${d.availabilityZones} availability ${d.availabilityZones === 1 ? "zone" : "zones"}`);36 if (d.launchedOn) parts.push(`launched ${fmtDate(d.launchedOn)}`);37 parts.push(`status ${CLOUD_STATUS_LABEL[d.status as keyof typeof CLOUD_STATUS_LABEL] ?? d.status}${d.isSovereign ? ", sovereign" : ""}`);38 return `${parts.join(" — ")}. Source-linked, city-level location.`;39}4041export async function generateMetadata({ params }: { params: Params }): Promise<Metadata> {42 const { slug } = await params;43 const r = await api.cloudRegion(slug);44 if (!r.ok) return r.status === 404 ? NOT_FOUND_METADATA : { title: "Cloud region" };45 const d = r.data;46 return pageMetadata({ title: `${d.provider.name} ${d.code} — ${d.name}`, description: describe(d), path: routes.cloudRegion(d.slug) });47}4849export default async function CloudRegionPage({ params }: { params: Params }) {50 const { slug } = await params;51 const [r, countryHref] = await Promise.all([api.cloudRegion(slug), countryHrefMap()]);52 if (!r.ok && r.status === 404) notFound();53 if (!r.ok) {54 return (55 <Page className="py-6">56 <Breadcrumbs items={[{ name: "Cloud regions", href: routes.cloudRegions() }, { name: slug, href: routes.cloudRegion(slug) }]} className="mb-4" />57 <OfflineNotice detail={r.error} />58 </Page>59 );60 }61 const d = r.data;62 const providerHref = `${routes.cloudRegions()}?provider=${encodeURIComponent(d.provider.slug)}`;63 const cHref = countryHref(d.countryIso2);64 const cName = d.countryName ?? (d.countryIso2 ? countryName(d.countryIso2) : null);65 const siblings = (d.siblingRegions ?? []).filter((s) => s.id !== d.id);66 const hosts = d.hostFacilities ?? [];67 const facilities = (d.marketFacilities?.length ? d.marketFacilities : d.facilitiesInMetro) ?? [];68 const events = d.events ?? [];69 const provenance = d.provenance ?? [];70 const ids = Object.entries(d.externalIds ?? {}).filter(([k]) => k !== "kind");71 const hasGeo = typeof d.lat === "number" && typeof d.lng === "number";7273 const jsonLd = compactJsonLd({74 "@context": "https://schema.org",75 "@type": "Place",76 name: `${d.provider.name} ${d.code} — ${d.name}`,77 url: absoluteUrl(routes.cloudRegion(d.slug)),78 description: describe(d),79 geo: hasGeo ? { "@type": "GeoCoordinates", latitude: d.lat, longitude: d.lng } : undefined,80 address: d.city || cName ? compactJsonLd({ "@type": "PostalAddress", addressLocality: d.city, addressCountry: d.countryIso2 }) : undefined,81 containedInPlace: cName ? { "@type": "Country", name: cName } : undefined,82 sameAs: d.sourceUrl ?? undefined,83 });8485 const nav = [86 { id: "location", label: "Location" },87 { id: "siblings", label: `Other ${d.provider.name} regions`, count: siblings.length },88 { id: "hosting", label: hosts.length ? "Host facilities" : "Hosting" },89 { id: "facilities", label: "Facilities in this market", count: facilities.length },90 { id: "events", label: "Events", count: events.length },91 { id: "provenance", label: "Provenance", count: provenance.length },92 { id: "identifiers", label: "Identifiers", count: ids.length },93 { id: "source", label: "Source" },94 ];9596 return (97 <Page>98 <JsonLd data={jsonLd} />99 <Breadcrumbs items={[{ name: "Cloud regions", href: routes.cloudRegions() }, { name: d.provider.name, href: providerHref }, { name: d.code, href: routes.cloudRegion(d.slug) }]} className="pt-4" />100101 <header className="py-4 sm:py-6">102 <div className="label-caps mb-1.5">103 <Link href={routes.operator(d.provider.slug)} className="hover:text-ink">104 {d.provider.name}105 </Link>{" "}106 · Cloud region107 </div>108 <h1 className="text-[22px] font-semibold leading-tight tracking-[-0.02em] text-ink sm:text-[28px]">{d.name}</h1>109 <div className="mt-2 flex flex-wrap items-center gap-2">110 <Badge tone="outline" mono>111 {d.code}112 </Badge>113 <CloudStatusBadge status={d.status} size="sm" />114 {d.isSovereign && <Badge tone="accent">Sovereign</Badge>}115 <PrecisionBadge precision={d.geoPrecision} />116 </div>117 <p className="mt-2 flex flex-wrap items-center gap-x-1.5 text-[13px] text-ink-2">118 {d.countryIso2 && <CountryFlag iso2={d.countryIso2} size={13} />}119 {d.city && <span>{d.city}</span>}120 {d.regionName && d.regionName !== d.city && <span className="text-ink-3">· {d.regionName}</span>}121 {cName && (122 <>123 <span className="text-ink-4">·</span>124 {cHref ? (125 <Link href={cHref} className="hover:text-ink hover:underline">126 {cName}127 </Link>128 ) : (129 <span>{cName}</span>130 )}131 </>132 )}133 {d.metro && (134 <>135 <span className="text-ink-4">·</span>136 <Link href={routes.market(d.metro.slug)} className="hover:text-ink hover:underline">137 {d.metro.name} market138 </Link>139 </>140 )}141 </p>142 <p className="mt-1 text-[12px] text-ink-3">Location shown at {PRECISION_LABEL[d.geoPrecision].toLowerCase().replace(" location", "")} — cloud providers publish the metro, not the building.</p>143 </header>144145 <MetricRow cols={4}>146 <MetricCell>147 <Metric value={d.availabilityZones ?? "—"} label="Availability zones" note={d.availabilityZones === null ? "not published" : undefined} />148 </MetricCell>149 <MetricCell>150 <Metric value={fmtDate(d.launchedOn)} label="Launched" note={d.launchedOn ? undefined : "not published"} />151 </MetricCell>152 <MetricCell>153 <Metric value={fmtDate(d.announcedOn)} label="Announced" note={d.announcedOn ? undefined : "not published"} />154 </MetricCell>155 <MetricCell>156 <Metric value={fmtInt(siblings.length)} label={`Other ${d.provider.name} regions`} note={`${fmtInt(facilities.length)} indexed ${facilities.length === 1 ? "facility" : "facilities"} in this market`} />157 </MetricCell>158 </MetricRow>159160 <SectionNav items={nav} className="mt-4" />161162 <div className="mt-6 flex flex-col gap-8">163 <Section id="location" title="Location" description="Published location of the region; coordinates are city-level and never point at a physical data center." className="scroll-mt-28">164 <div className="grid gap-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]">165 <KeyValueList166 items={[167 { label: "City", value: d.city },168 { label: "Region", value: d.regionName },169 { label: "Country", value: cName ? cHref ? <Link href={cHref} className="hover:text-accent hover:underline">{cName}</Link> : cName : null },170 { label: "Market", value: d.metro ? <Link href={routes.market(d.metro.slug)} className="hover:text-accent hover:underline">{d.metro.name}</Link> : null },171 { label: "Coordinates", value: hasGeo ? <span className="figure">{d.lat!.toFixed(2)}, {d.lng!.toFixed(2)}</span> : null },172 { label: "Precision", value: PRECISION_LABEL[d.geoPrecision] },173 ]}174 />175 {hasGeo ? <EntityMap center={{ lat: d.lat!, lng: d.lng! }} precision="city" zoom={9} filters={d.countryIso2 ? { country: d.countryIso2 } : undefined} openLabel="Open market in map" className="h-[220px] md:h-full md:min-h-[260px]" /> : <EmptyState compact title="No published coordinates" description="The provider lists this region without a city we could place." />}176 </div>177 </Section>178179 {siblings.length > 0 && (180 <Section id="siblings" title={`Other ${d.provider.name} regions`} description={`${siblings.length} ${siblings.length === 1 ? "region" : "regions"} indexed for this provider`} className="scroll-mt-28" aside={<Link href={providerHref} className="text-[12px] text-ink-3 hover:text-ink">All {d.provider.name} regions</Link>}>181 <Table dense mobileCollapse={{ total: siblings.length, label: "regions" }}>182 <THead>183 <tr>184 <Th>Code</Th>185 <Th>Region</Th>186 <Th className="hidden sm:table-cell">City</Th>187 <Th>Country</Th>188 <Th numeric className="hidden md:table-cell">189 AZs190 </Th>191 <Th>Status</Th>192 </tr>193 </THead>194 <TBody>195 {siblings.map((s) => (196 <Tr key={s.id}>197 <Td primary>198 <RowLink href={routes.cloudRegion(s.slug)} className="figure text-[12.5px]">199 {s.code}200 </RowLink>201 <div className="text-[11.5px] text-ink-3 sm:hidden">{s.name}</div>202 </Td>203 <Td label="Region" className="hidden text-ink-2 sm:table-cell">204 {s.name}205 </Td>206 <Td label="City" className="hidden text-ink-3 sm:table-cell">207 {s.city ?? "—"}208 </Td>209 <Td label="Country">210 <CountryFlag iso2={s.countryIso2} withName className="max-w-[160px] text-ink-2" />211 </Td>212 <Td label="AZs" numeric className="hidden md:table-cell">213 {s.availabilityZones ?? <span className="text-ink-4">—</span>}214 </Td>215 <Td label="Status">216 <CloudStatusBadge status={s.status} />217 </Td>218 </Tr>219 ))}220 </TBody>221 </Table>222 </Section>223 )}224225 <Section id="hosting" title={hosts.length ? "Host facilities (publicly verified)" : "Hosting"} description={hosts.length ? "Facilities a source publicly states host this region." : "Cloud providers publish the metro, not the building."} className="scroll-mt-28">226 {hosts.length ? (227 <FacilityTable items={hosts} columns={["operator", "country", "status", "mw"]} />228 ) : (229 <p className="text-[13.5px] text-ink-2">230 Region associated with {d.metro ? <>the <Link href={routes.market(d.metro.slug)} className="font-medium text-ink hover:text-accent hover:underline">{d.metro.name}</Link> market</> : cName ? <>a market in {cName}</> : <>a market</>} — not hosted at a specific facility.231 </p>232 )}233 <ApiNote note={d.note} className="mt-3" />234 </Section>235236 <Section id="facilities" title="Facilities in this market" description={d.metro ? `Indexed data centers in the ${d.metro.name} market — not necessarily hosting this region.` : "Indexed data centers around the published location — not necessarily hosting this region."} className="scroll-mt-28">237 {facilities.length ? <FacilityTable items={facilities} columns={["operator", "country", "status", "mw"]} /> : <HonestEmpty title="No indexed facility in this metro yet" detail="facilities appear as operator, registry and dataset connectors observe them" />}238 </Section>239240 <Section id="events" title="Events" description="Announcement, launch and update events detected for this region." className="scroll-mt-28">241 <EventList events={events} dense emptyTitle="No event recorded for this region" emptyDescription="Announcements and launches appear here when the provider's region list changes." />242 </Section>243244 <Section id="provenance" title="Provenance" description="Field by field: which source backs which value, with first / last observation." className="scroll-mt-28">245 <ProvenanceTable items={provenance} emptyDescription="No field-level provenance has been published for this region yet." />246 </Section>247248 {ids.length > 0 && (249 <Section id="identifiers" title="Identifiers" description="Provider identifiers as published." className="scroll-mt-28">250 <KeyValueList items={ids.map(([k, v]) => ({ label: k.replace(/_/g, " "), value: <span className="figure text-[12.5px]">{String(v)}</span> }))} columns={2} />251 </Section>252 )}253254 <Section id="source" title="Source" className="scroll-mt-28">255 {d.sourceUrl ? (256 <p className="text-[13px] text-ink-2">257 Provider documentation: <ExtLink href={d.sourceUrl} />258 </p>259 ) : (260 <p className="text-[13px] text-ink-3">No source URL recorded for this region.</p>261 )}262 </Section>263 </div>264265 <SourcesFooter sources={r.sources} updatedAt={d.updatedAt} note="Region facts are taken verbatim from the provider's published region list; availability-zone counts and launch dates are shown only when published." />266 </Page>267 );268}269